package com.app.designpattern

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import com.app.designpattern.Behavioral.templatemethod.DesignPatternCourse
import com.app.designpattern.Behavioral.templatemethod.FECourse
import com.app.designpattern.creation.builder.Course
import com.app.designpattern.creation.factory.abstractfactory.JavaCourseFactory
import com.app.designpattern.creation.factory.abstractfactory.PythonCourseFactory
import com.app.designpattern.creation.factory.factorymethod.JavaVideoFactory
import com.app.designpattern.creation.factory.factorymethod.PythonVideoFactory
import com.app.designpattern.creation.factory.singlefactory.JavaVideo
import com.app.designpattern.creation.factory.singlefactory.VideoFactory
import com.app.designpattern.creation.prototype.clone.Pig
import com.app.designpattern.creation.singleton.T
import com.app.designpattern.designprinciples.demeter.Ceo
import com.app.designpattern.designprinciples.demeter.Leader
import com.app.designpattern.designprinciples.dependencies.AndroidLanguage
import com.app.designpattern.designprinciples.dependencies.JavaLanguage
import com.app.designpattern.designprinciples.dependencies.PythonLanguage
import com.app.designpattern.designprinciples.dependencies.Zhangjw
import com.app.designpattern.designprinciples.openclose.AndroidBook
import com.app.designpattern.designprinciples.openclose.AndroidDiscountBook
import com.app.designpattern.structural.adapter.classadapter.Adapter
import com.app.designpattern.structural.adapter.classadapter.Target
import com.app.designpattern.structural.adapter.instanceadapter.Adapter2
import com.app.designpattern.structural.adapter.instanceadapter.Target2
import com.app.designpattern.structural.bridge.ABCBank
import com.app.designpattern.structural.bridge.ACBCBank
import com.app.designpattern.structural.bridge.CloseAccount
import com.app.designpattern.structural.bridge.OpenAccount
import com.app.designpattern.structural.composite.CataLoagComponte
import com.app.designpattern.structural.composite.CatalogCursor
import com.app.designpattern.structural.composite.Cursor
import com.app.designpattern.structural.decorator.v1.Battercake
import com.app.designpattern.structural.decorator.v1.BattercakeWithEgg
import com.app.designpattern.structural.decorator.v1.BattercakeWithHotDog
import com.app.designpattern.structural.decorator.v2.ABattercake
import com.app.designpattern.structural.decorator.v2.Battercake2
import com.app.designpattern.structural.decorator.v2.EggDecorator
import com.app.designpattern.structural.decorator.v2.HotDogDecorator
import com.app.designpattern.structural.facade.GoodsChangeService
import com.app.designpattern.structural.facade.PointGood
import com.app.designpattern.structural.flyweight.EmployeeFactory
import com.app.designpattern.structural.flyweight.Manager
import com.app.designpattern.structural.proxy1.Car
import com.app.designpattern.structural.proxy1.CarLogProxy
import com.app.designpattern.structural.proxy1.CarTimeProxy
import com.app.designpattern.structural.proxy2.Car2
import com.app.designpattern.structural.proxy2.Moveable2
import com.app.designpattern.structural.proxy2.TimeHandler
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.reflect.Proxy
import java.util.*

class MainActivity : AppCompatActivity(), Cloneable {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        openclose?.setOnClickListener {
            //开发和关闭原则(对扩展开放,对修改关闭)
            //1+
            //bufix 222222
            //bufix 1111111


            var androidBook = AndroidBook(44, "Android", 99.0)
            Log.i("opemclose", "课程id:${androidBook.id} 课程名称:${androidBook.name} 课程的价格:${androidBook.price}")
            //价格我现在业务需要是android相关的书籍需要打折??
            //第一种方法 :在接口中新增一个获取打折价格的方法,androidbook类也需要修改,当前方法也需要修改
            //这样就违背了对修改关闭的原则,假如我相关的java书籍,python书籍都需要打折,那么所有的子类都需要修改,工作量大,容错率高
            //第二种方法,扩展一个androidbook的子类,里面新增扩展方法,折扣的方法
            var androidDiscountBook = AndroidDiscountBook(44, "androiddisbook", 99.0)
            Log.i(
                "opemclose",
                "课程id:${androidDiscountBook.id} 课程名称:${androidDiscountBook.name} 课程的原价:${androidDiscountBook.price} 课程的折后价:${androidDiscountBook.getDiscountPrice()}"
            )
        }
        dependencies?.setOnClickListener {
            //xiug l zhuianijia
            //依赖倒置的原则
            //有一个zhangjw的类,他有学习java语言的,他也正在学习python语言
            /* var zhangjw = Zhangjw()
             zhangjw.studyJavaLanguage()
             zhangjw.studyPythonLanguage()*/
            //但是现在,他又准备学习android语言,??
            //第一种方法:在zhangjw类中新增学习android的方法,但是这样就违背了依赖倒置的原则,调用者依赖实例类的方法实现,上层依赖了下层
            //第二种,面向抽象编程(抽象:接口和抽象类)
            //怎么做呢:根据业务需求,把各类语音共同实现一个语音学习的接口,不关心具体细节,
            var zhangjw = Zhangjw()
            //具体学习什么语音,交给上层去指定
            zhangjw.getStudyLanguage(JavaLanguage())
            //第二种方法实现
            zhangjw.setLanguage(PythonLanguage())
            //如果现在要学android,只需要新增android类实现语音接口就好
            zhangjw.setLanguage(AndroidLanguage())
        }
        demeter?.setOnClickListener {
            //迪米特原则,就是当前类,只管朋友的关系,朋友:成员变量,方法的入参和出参
            var ceo = Ceo()
            ceo.commidLeaderCheckLanguageNumber(Leader())
        }
        singlefactory?.setOnClickListener {
            //简单工厂,不属于23中设计模式,但是他可以更好的引申出工厂方法模式和抽象工厂模式
            //优点:客户端不需要知道具体的生成细节,只需要传入type就可以获取对应type的实例,降得了耦合
            //缺点:如果新增type类型,简单工厂的方法就需要改变,不符合开闭原则,当然可以使用反射去一定程度上适应开闭原则
            /*var videoFactory = VideoFactory1()
            val video = videoFactory.getVideo("java")
            video?.getProduct()*/
            //使用静态方法和反射,一定程度上适应开闭原则
            val video = VideoFactory.getVideo(JavaVideo::class.java)
            video?.getProduct()
        }
        factorymethod?.setOnClickListener {
            //工厂方法设计模式
            //创建一个创建对象的接口(接口或者抽象类),具体创建产品实例交给其接口的子类去具体实现
            var videofactory = JavaVideoFactory()
            val video = videofactory.getVideo()
            video.product()
            var videoFactory1 = PythonVideoFactory()
            val video1 = videoFactory1.getVideo()
            video1.product()

            //这样的好处是:符合开闭原则,如果新增其他视频,只需要创一个产品类和对应的产品工厂类即可,其他类都不需要修改
            //缺点:如果产品种类多,就会引起类的繁多,
        }
        abfactory?.setOnClickListener {
            //抽象工厂设计模式
            //对一组产品结构等级的生成即产品族
            var courseFactory = JavaCourseFactory() //指定具体的工厂,生产出属于该工厂的不同的产品等级
            val note = courseFactory.getNote()
            val video = courseFactory.getVideo()
            note.product()
            video.product()
            var courseFactory1 = PythonCourseFactory()
            val video1 = courseFactory1.getVideo()
            val note1 = courseFactory1.getNote()
            video1.product()
            note1.product()
        }
        builder?.setOnClickListener {
            //建造者设计模式
            //把一个复杂的对像的构建和表示分离,相同的构建可以有不同的表示
            //一个复杂的对像,有很多属性(成员变量),并且构建时可以指定属性构建
            var course = Course.Builder().builderCourseName("java设计模式").builderCourseVideo("java设计模式视频").builder()
            Log.i("builder", "$course")
            //在android中okhttpClient 和request 都是使用的建造者设计模式,都是复杂的对像,属性很多,我们构建的时候,可以指定不同的参数,形成相同的对像的不同的表示形式
//            var okHttpClient = OkHttpClient.Builder().connectTimeout(5000, TimeUnit.SECONDS).build()
//            var request = Request.Builder().addHeader("token", "").build()
//            var stringBuilder = StringBuilder().append(10).append("11").append(12.0)
        }
        lazysingleton?.setOnClickListener {
            var thread1 = Thread(T())
            var thread2 = Thread(T())
            thread1.start()
            thread2.start()
            Log.i("singleton", "执行完毕了")
        }
        cloneable?.setOnClickListener {
            //原型设计模式
            //就是把当前的类实现cloneable接口,重写他的clone(),并且克隆的对像不会走构造方法
            //如果一个抽象类实现了cloneable接口,并且重写了clone()方法,那个他的子类也具有克隆的功能
            //下面的例子是浅克隆,只克隆对像,没有对具体的属性(引用类的的变量)进行处理
            /* var mail = Mail()
             mail.content = "初始化模板"
             for (i in 0..10) {
                 var mailTemp = mail.clone() as Mail
                 mailTemp.name = "姓名" + 1
                 mailTemp.emailAddress = "$i@163.com"
                 mailTemp.content = "恭喜你中奖了"
                 MailUtils.sendMail(mailTemp)
             }
             MailUtils.saveMail(mail)*/

            //shenk深克隆:把具体的引用类型,重新clone出来并赋值,这样就是不同的对像
            var brithday = Date(0L)
            var pig1 = Pig("乔治", brithday)
            var pig2 = pig1.clone()
            Log.i("clone", pig1.toString())
            Log.i("clone", pig2.toString())
            pig1.brithday = Date(6666666666666L)

            Log.i("clone", pig1.toString())
            Log.i("clone", pig2.toString())


        }
        facead?.setOnClickListener {
            //外观模式
            //符合迪米特原则,最少知道原则;
            //应用层通过一个外观类,跟外观类中的子系统进行通信,应用层不需要知道具体的子系统,

            //第一不使用外观类的情况
            var goodsChange = GoodsChangeService()
            var pointGoods = PointGood()
            pointGoods.name = "手机壳"
            /*//不使用外观类的情况:就需要应用层,创建出子系统对像,这样不符合迪米特原则,也不是外观模式的最佳实现
            goodsChange.setPointPaymentService(PointPaymentService())
            goodsChange.setQualifyService(QualifyService())
            goodsChange.setShippingService(ShippingService())*/
            //二:在外观类中,直接初始化子系统实例,应用层,无需知道子系统的实例

            goodsChange.goodsChang(PointGood())
        }
        decorator?.setOnClickListener {
            //装饰者设计模式
            //v1不使用装饰者模式
            var battercake = Battercake()
            Log.i("decorator", "${battercake.desc()}  ${battercake.cost()}")
            var battercakeWithEgg = BattercakeWithEgg()
            Log.i("decorator", "${battercakeWithEgg.desc()}  ${battercakeWithEgg.cost()}")
            var battercakewithHotdog = BattercakeWithHotDog()
            Log.i("decorator", "${battercakewithHotdog.desc()}  ${battercakewithHotdog.cost()}")
            //如果业务需求,需要加俩个鸡蛋,俩个热狗,则无法实现

            //v2,使用装饰者模式
            //装饰者有四个关键点:被装饰的抽象实体,被装饰的具体实体类,抽象的装饰者和具体的装饰者对像
            var battercake2 = Battercake2() as ABattercake
            battercake2 = EggDecorator(battercake2)
            battercake2 = EggDecorator(battercake2)
            battercake2 = HotDogDecorator(battercake2)
            Log.i("decorator", "${battercake2.desc()}  ${battercake2.cost()}")
        }
        adapter?.setOnClickListener {
            //适配器设计模式
            //类继承的方式
            var adapter = Adapter() as Target
            adapter.request()

            //组合的方式
            var adapter2 = Adapter2() as Target2
            adapter2.request()
        }

        flyweight?.setOnClickListener {
            //享元设计模式:一种池技术的操作,使用享元可以避免创建更多的对像,减少内存的消耗
            var department = arrayOf("pm", "bd", "Cd")

            var emplee = EmployeeFactory.getManager(department[((Math.random() * department.size).toInt())]) as Manager
            emplee.report()
        }
        component?.setOnClickListener {
            //组合模式:把对像组合成一种树状图的结构,如一个课程目录里面包括windows课程和java课程目录,java课程目录中又包括很多课程
            //我自己的课程目录
            var myCatalog = CatalogCursor("我的课程目录") as CataLoagComponte
            //window的课程
            var windowCataLoagComponte1 = Cursor("windown课程", 110.0) as CataLoagComponte
            //java的课程目录
            var javacatalog = CatalogCursor("java课程目录") as CataLoagComponte
            //javaee课程
            var javaee = Cursor("javaee课程", 120.0) as CataLoagComponte
            //设计模式课程
            var designPattern = Cursor("设计模式", 321.0) as CataLoagComponte
            //java课程目录中包括javaee和设计模式
            javacatalog.add(javaee)
            javacatalog.add(designPattern)

            //我的课程目录中包括window课程和java课程目录
            myCatalog.add(windowCataLoagComponte1)
            myCatalog.add(javacatalog)

        }

        bridge?.setOnClickListener {
            //桥接模式:俩个对像分别像桥的俩头,中间用组合的方法注入(构造器注入,set方式注入)
            var bank = ABCBank(CloseAccount())
            bank.openAccount()
            var bank2 = ABCBank(OpenAccount())
            bank2.openAccount()
            var bank3 = ACBCBank(CloseAccount())
            bank3.openAccount()
            var bank4 = ACBCBank(OpenAccount())
            bank4.openAccount()
        }

        proxy1?.setOnClickListener {
            //静态代理:为其他对像提供一种代理一控制对这个对像的访问
            //代理类CarXXXProxy和被代理类Car 都实现相同的接口或者集成相同的抽象类,
            //你要代理谁就产生该代理的实例,就把被代理对像传递进来

            //代理car的run方法,并加上前后的代理类的处理逻辑
            var car = Car()
            var cartimeProxy1 = CarTimeProxy(car)
            cartimeProxy1.run()
            Log.i("proxy1", "-------------------------")
            //代理层叠
            var car2 = Car()
            var carLogProxy = CarLogProxy(car2)
            var carTimeProxy = CarTimeProxy(carLogProxy)
            carTimeProxy.run()
        }
        proxy2?.setOnClickListener {
            //动态代理
            var car = Car2()
            val javaClass = car.javaClass
            var timeHandler = TimeHandler(car)
            /**
             * 参数
             * 1.代理类的classloader
             * 2.代理类实现的接口
             * 3.InvocationHandler的实现类
             */
            val moveable2 =
                Proxy.newProxyInstance(javaClass.classLoader, javaClass.interfaces, timeHandler) as Moveable2
            moveable2.run()
        }
        method?.setOnClickListener {
            //模板方法设计模式
            //一个抽象类里有一个final修改的方法,该方法,子类不可从写,里面设计了一些固定的算法,
            var des = DesignPatternCourse()
            des.makeCourse()
            var fe = FECourse(false)
            fe.makeCourse()
        }
    }

}
